package com.czt.mail.limit;

import java.util.LinkedList;

/**
 * @author czt
 * @version 1.0
 * {@desc} 滑动时间窗口，虽然窗口是固定的，但窗口的起止时间是动态的，他是限定了过去多长时间内请求不能超过n
 * @date 2024/8/22 09:37
 */
public class SlidingTimeWindowLimiter {

    /**
     * 用于记录时间窗口内已请求的次数，值总与paneLength中所以元素之和保持一致
     */
    private int totalCounter = 0;

    /**
     * 窗格数
     */
    private final int paneNum;

    /**
     * 窗格时长，，单位ms
     */
    private final long paneLength;

    /**
     * 触发限流的阈值
     */
    private final int limitThreshold;

    /**
     * 窗格集合，值为窗格内的请求
     */
    private final LinkedList<Integer> paneList = new LinkedList<>();

    /**
     * 窗口右边界
     */
    private long windowRightBorder;

    public SlidingTimeWindowLimiter(int paneNum, long paneLength, int limitThreshold) {
        this.paneNum = paneNum;
        this.paneLength = paneLength;
        this.limitThreshold = limitThreshold;
        this.paneList.addLast(0);
        this.windowRightBorder = System.currentTimeMillis();
    }

    public synchronized boolean limit() {
        slidePaneIfNecessary();
        if ((totalCounter + 1) > limitThreshold) {
            return true;
        }
        totalCounter++;
        paneList.set(paneList.size() - 1, paneList.getLast() + 1);
        System.out.println("paneList = " + paneList);
        return false;
    }

    /**
     * 滑动窗格，如果条件满足
     */
    private void slidePaneIfNecessary() {
        long now = System.currentTimeMillis();
        while (this.windowRightBorder < now) {
            this.windowRightBorder += this.paneLength;
            // 初始化窗格请求次数为0
            this.paneList.addLast(0);
            if (this.paneList.size() > this.paneNum) {
                this.totalCounter -= this.paneList.getFirst();
                this.paneList.removeFirst();
            }
        }
    }


}
